package com.sina.mysql.advice;

import com.sina.mysql.config.TransactionDefinitionHolder;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import static org.testng.Assert.assertNotNull;

/**
 * @ClassName TransactionDefinitionInterceptor
 * @Description:
 * @Author 段浩杰
 * @Date 2018/8/22 17:28
 * @Version 1.0
 */
public class TransactionDefinitionInterceptor implements MethodInterceptor, InitializingBean, ApplicationContextAware {

    private static final Logger LOG = LoggerFactory.getLogger( TransactionDefinitionInterceptor.class );

    private TransactionAttributeSource transactionAttributeSource;

    private ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        transactionAttributeSource = applicationContext.getBean( TransactionAttributeSource.class );
        assertNotNull( transactionAttributeSource, "config error" );
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass( invocation.getThis() ) : null);

        if (targetClass != null && !TransactionSynchronizationManager.isActualTransactionActive()) {
            LOG.debug( "外层事务:开始{},{}", invocation.getMethod(), targetClass );
            TransactionDefinition definition = transactionAttributeSource.getTransactionAttribute(
                    invocation.getMethod(), targetClass );
            TransactionDefinitionHolder.set( definition );
        } else {
            LOG.debug( "内层事务:开始{},{}", invocation.getMethod(), targetClass );
        }

        Throwable e = null;
        Object result = null;
        try {

            result = invocation.proceed();

        } catch (Throwable throwable) {
            e = throwable;
        } finally {
            if (!TransactionSynchronizationManager.isActualTransactionActive()) {
                LOG.debug( "外层事务结束:{},{}", invocation.getMethod(), targetClass );
                // 事务结束
                TransactionDefinitionHolder.clear();
            } else {
                LOG.debug( "内层事务:线束{},{}", invocation.getMethod(), targetClass );
            }

        }


        if (e != null) {
            throw e;
        }

        return result;
    }


}
